feat(tools): add SDK idempotency keys for safe memory write retries - #1642
Open
Sravanjangam wants to merge 1 commit into
Open
feat(tools): add SDK idempotency keys for safe memory write retries#1642Sravanjangam wants to merge 1 commit into
Sravanjangam wants to merge 1 commit into
Conversation
Phase A — SDK-only, Fixes supermemoryai#1627. Generate Idempotency-Key = SHA256(normalizedContent|sorted tags|minuteBucket) and attach as Idempotency-Key header on addMemory / documentAdd via Supermemory client RequestOptions. Header is optional for the backend (Phase B will honor it). Prevents duplicate memories on network retries, enables safe retries and offline queue. Staff improvements: customIdempotencyKey (user-provided priority), RetryContext (reuses key across minute rollover), NFC+trim normalization, Why SHA-256 rationale, 14 Vitest tests. Co-authored-by: Sravanjangam <163002695+Sravanjangam@users.noreply.github.com>
Contributor
Author
|
Successor to #1628 — original PR was auto-closed by |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds SDK-level idempotency keys for safe memory write retries — Phase A (SDK-only) of the Hierarchy. Generates a stable
Idempotency-Keyheader on everyaddMemory/documentAddcall so network retries and caller retries do not create duplicate memories. Updated with Staff improvements: custom key,RetryContext, NFC normalization, 14 Vitest tests.Fixes #1627
Problem
packages/tools/src/ai-sdk.ts:addMemoryToolanddocumentAddTool(andopenai/tools.ts) calledclient.add({ content, containerTags })with no idempotency signal. On retry (thesupermemoryclient retries 2× on 5XX/network, plus caller retries), the backend creates duplicate memories/embeddings/writes.packages/tools/src/ai-sdk.ts:102—client.addwith noIdempotency-Keypackages/tools/src/openai/tools.ts:290— sameImpact
content|tags|minuteBuckethash prevents the commonfetch failed → retry → duplicateloop on flaky networks/mobile.RetryContextcover both one-off and session-scoped retries.Solution — Phase A (SDK-only, mergeable now, no backend change)
New file
packages/tools/src/shared/idempotency.ts(96 lines, zero deps):Why SHA-256?
Core helpers:
generateIdempotencyKey(content, containerTags, now?, customKey?)— uses Web Cryptocrypto.subtle(Node 20+).customKeypriority: user-provided → generated (ifcustomKeyis non-empty, returned as-is).buildIdempotencyHeaders(content, containerTags, now?, customKey?) → { "Idempotency-Key": key }createRetryContext(content, containerTags, now?, customKey?) → { key, headers, getKey(), getHeaders() }— future-proof retry helper that captures the key once and reuses it across retries, even across minute rollovers:Wired in
packages/tools/src/ai-sdk.ts(addMemoryTool+documentAddToolnow accept optionalidempotencyKeyinput) andpackages/tools/src/openai/tools.ts:Header is optional for the backend — ignored until Phase B honors it, so this PR is useful even before server support. No breaking change, no new package. Tool
addMemorynow exposesidempotencyKey?: stringinput for developers who already have a key.Benchmark
Key generation is
SHA-256of a short string — ~0.05–0.1 ms per call (measured locally via Vitest,crypto.subtle); negligible vs. the network call it protects. No impact onsearchMemoriesor other tools.addMemoryheaderIdempotency-Key: <64-hex>Failure Handling
content.normalize("NFC").trim()before hashing — Unicode stable (caféNFC ==cafe\u0301NFD) and trailing whitespace stable ("hello "=="hello"), cross-platform stable.|tags|bucket).containerTagsorder independence — sorted before hashing so["b","a"]and["a","b"]produce the same key.RetryContextintentionally reuses the captured key across rollovers for safe retry.nowinjection for testing and for callers that want to control bucketing.Memory Footprint
Stateless — no cache, no storage. One
SHA-256per write (~64 bytes hex), plus optionalRetryContext(~80 bytes). No growth.Testing
Real Vitest —
bun x vitest run packages/tools/src/shared/idempotency.test.ts— 14 tests, 5 ms:nowinjection respectedIdempotency-KeyBiome:
bun x biome check packages/tools/src/shared/idempotency.ts— clean.Typecheck:
bun x tsc --noEmit --project packages/tools/tsconfig.json— no new errors (pre-existingai-sdk.tstool overload errors verified onmainviagit stash).Vitest: 14/14 pass (was 10/10, +4 Staff-requested edge cases).
Environment
supermemory@3.0.0-alpha.26fix/idempotent-memory-writes@a84003f1(force-pushed toSravanjangam/supermemory)supermemoryai/supermemory@main d436792eNon-goals (Phase A)
userIdin hash (backend can hashapiKeyserver-side if needed)Deferred roadmap
Idempotency-Key(dedupe store,409or200replay)customIdintegration